#include "serialport.h"
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <error.h>
#include <logger/logger.h>
#include <string.h>

namespace cores
{
    namespace devices
    {
        std::map<int, int> SerialPort::gBaudRateMap =
            {
                {1200, B1200},
                {2400, B2400},
                {4800, B4800},
                {9600, B9600},
                {19200, B19200},
                {38400, B38400}};

        std::map<int, int> SerialPort::gDatabitsMap =
            {
                {5, CS5},
                {6, CS6},
                {7, CS7},
                {8, CS8}};

        SerialPort::SerialPort(const std::string& dev_name, const int &baudRate, const int &dataBits)
            : _portName(dev_name)
            , _baud_rate(baudRate)
            , _data_bit(dataBits)
        {
            open_port();
        }

        SerialPort::~SerialPort()
        {
            this->close_port();
        }

        bool SerialPort::isOpen() const
        {
            return fd > 0;
        }

        void SerialPort::open_port()
        {
            this->fd = ::open(this->_portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
            if (this->fd < 0)
            {
                XERROR("Error opening serialport [\"%s\"]: %s", this->_portName.c_str(), strerror(errno));
                return;
            }

            struct termios options;
            ::tcgetattr(this->fd, &options);
        
            int baudRateT = transformBaud(_baud_rate);
            if (baudRateT < 0)
            {
                this->ConnStatus =  BAUD_NOT_SUPPORTED;
                XERROR("串口不支持波特率:%d", _baud_rate);
                return;
            }

            ::cfsetispeed(&options, baudRateT);
            ::cfsetospeed(&options, baudRateT);

            auto dataBits = transformDataBits(_data_bit);
            if (dataBits < 0)
            {
                this->ConnStatus = DATABITS_NOT_SUPPORTED;
                XERROR("串口不支持%d数据位", _data_bit);
                return;
            }

            options.c_cflag &= ~CSIZE;
            options.c_cflag |= dataBits;
            options.c_cflag |= (CLOCAL | CREAD);
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
            options.c_iflag &= ~(IXON | IXOFF | IXANY);
            options.c_oflag &= ~OPOST;
            options.c_iflag &= ~ICRNL;  // 禁止将输入的CR转换为NL
            options.c_iflag &= ~(IXON); // 清bit位 关闭流控字符

            ::tcsetattr(this->fd, TCSANOW, &options);
            this->ConnStatus = OK;
        }

        void SerialPort::close_port()
        {
            if (this->isOpen())
            {
                ::close(fd);
                fd = -1;
                this->ConnStatus = -1;
                XLOG("成功关闭串口[串口名:%s,波特率:%d, 数据位:%d]", _portName.c_str(), _baud_rate, _data_bit);
            }
        }

        int SerialPort::transformBaud(const int &baudrate)
        {
            if (SerialPort::gBaudRateMap.count(baudrate) == 0)
                return -1;
            return SerialPort::gBaudRateMap[baudrate];
        }

        int SerialPort::transformDataBits(const int &dataBits)
        {
            if (SerialPort::gDatabitsMap.count(dataBits) == 0)
                return -1;

            return SerialPort::gDatabitsMap[dataBits];
        }

        int SerialPort::readData(char * buffer, const size_t& size)
        {
            return ::read(this->fd, buffer, size);
        }

        int SerialPort::readData(unsigned char * buffer, const size_t& size)
        {
            return ::read(this->fd, buffer, size);
        }

        int SerialPort::writeData(const char* buffer, const size_t& size)
        {
            return ::write(this->fd, buffer, size);
        }

        int SerialPort::writeData(const unsigned char* buffer, const size_t& size)
        {
            return ::write(this->fd, buffer, size);
        }
    }
}